﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Drawing;

namespace LowLevelGraphics.ColorSpaces
{
    /// <summary>
    /// HSL Channel
    /// </summary>
    public enum HSLChannel    
    {
        /// <summary>
        /// 
        /// </summary>
        H = 0,
        /// <summary>
        /// 
        /// </summary>
        S = 1,
        /// <summary>
        /// 
        /// </summary>
        L = 2
    }

    /// <summary>
    /// Class to convert RGB to HSL and back (also called TSL)
    /// </summary>
    [DebuggerDisplay("H: {m_H}, S: {m_S}, L: {m_L}")]
    public class HSL : AbstractColorSpace
    {
        protected double m_H = 0.0f;
        protected double m_S = 0.0f;
        protected double m_L = 0.0f;

        /// <summary>
        /// primary constructor
        /// </summary>
        /// <param name="_color"></param>
        public HSL(Color _color)
            : base(_color)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HSL"/> class.
        /// </summary>
        /// <param name="_H">The _ H.</param>
        /// <param name="_S">The _ S.</param>
        /// <param name="_L">The _ L.</param>
        public HSL(double _H, double _S, double _L)
        {
            m_H = _H;
            m_S = _S;
            m_L = _L;
        }

        /// <summary>
        /// used by clone to create da deep copy
        /// </summary>
        /// <param name="_hsl"></param>
        internal HSL(HSL _hsl)            
        {
            this.m_H = _hsl.m_H;
            this.m_L = _hsl.m_L;
            this.m_S = _hsl.m_S;
        }

        /// <summary>
        /// Enable casting into operator
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static explicit operator HSL(Color _color)
        {
            return new HSL(_color);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static HSL FromColor(Color _color)
        {
            return new HSL(_color);
        }

        /// <summary>
        /// gets or sets H
        /// </summary>
        public double H
        {
            get { return m_H; }
            set { m_H = value; }
        }

        /// <summary>
        /// Gets or sets S
        /// </summary>
        public double S
        {
            get { return m_S; }
            set { m_S = value; }
        }

        /// <summary>
        /// Gets or sets L
        /// </summary>
        public double L
        {
            get { return m_L; }
            set { m_L = value; }
        }

        /// <summary>
        /// Transform rgb color into this color space
        /// </summary>
        protected override void CalcValues()
        {
            m_H = m_Color.GetHue();
            m_S = m_Color.GetSaturation();
            m_L = m_Color.GetBrightness();

            double themin, themax, delta;
            themin = Math.Min(m_Color.R, Math.Min(m_Color.G, m_Color.B));
            themax = Math.Max(m_Color.R, Math.Max(m_Color.G, m_Color.B));
            delta = themax - themin;

            m_L = (themin + themax) / 2;
            m_S = 0;
            if (m_L > 0 && m_L < 1)
                m_S = delta / (m_L < 0.5 ? (2 * m_L) : (2 - 2 * m_L));
            m_H = 0;
            if (delta > 0)
            {
                if (themax == m_Color.R && themax != m_Color.G)
                {
                    m_H += (m_Color.G - m_Color.B) / delta;
                }
                if (themax == m_Color.G && themax != m_Color.B)
                {
                    m_H += (2 + (m_Color.B - m_Color.R) / delta );
                }
                if (themax == m_Color.B && themax != m_Color.R)
                {
                    m_H += (4 + (m_Color.R - m_Color.G) / delta);
                }
            }
            //0.708333333f
        }

        /// <summary>
        /// retransform to rgb color space
        /// </summary>
        /// <returns></returns>
        public override Color ToColor()
        {
            HSL c1 = (HSL)this.Clone();

            ColorStruct c2, sat, ctmp;
            while (c1.m_H < 0)
            {
                c1.m_H += 360;
            }
            while(c1.m_H > 360)
            {
                c1.m_H -= 360;
            }
            if (c1.m_H < 120)
            {
                sat.R = (int)((120 - c1.m_H) / 60.0f);
                sat.G = (int)(c1.H / 60.0f);
                sat.B = 0;
            }
            else if (c1.m_H < 240)
            {
                sat.R = 0;
                sat.G = (int)((240 - c1.m_H) / 60.0f);
                sat.B = (int)((c1.m_H - 120) / 60.0f);
            }
            else
            {
                sat.R = (int)((c1.m_H - 240) / 60.0f);
                sat.G = 0;
                sat.B = (int)((360 - c1.m_H) / 60.0f);
            }
            sat.R = Math.Min(sat.R, 1);
            sat.G = Math.Min(sat.G, 1);
            sat.B = Math.Min(sat.B, 1);

            ctmp.R = (int)(2 * c1.m_S * sat.R + (1 - c1.m_S));
            ctmp.G = (int)(2 * c1.m_S * sat.G + (1 - c1.m_S));
            ctmp.B = (int)(2 * c1.m_S * sat.B + (1 - c1.m_S));

            if (c1.m_L < 0.5f)
            {
                c2.R = (int)(c1.m_L * ctmp.R);
                c2.G = (int)(c1.m_L * ctmp.G);
                c2.B = (int)(c1.m_L * ctmp.B);
            }
            else
            {
                c2.R = (int)((1 - c1.m_L) * ctmp.R + 2 * c1.m_L - 1);
                c2.G = (int)((1 - c1.m_L) * ctmp.G + 2 * c1.m_L - 1);
                c2.B = (int)((1 - c1.m_L) * ctmp.B + 2 * c1.m_L - 1);
            }

            if (c2.R < 0) c2.R = 0; if (c2.R > 255) c2.R = 255;
            if (c2.G < 0) c2.G = 0; if (c2.G > 255) c2.G = 255;
            if (c2.B < 0) c2.B = 0; if (c2.B > 255) c2.B = 255;
            return Color.FromArgb(c2.R, c2.G, c2.B);
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("Color H = {0}, S = {1}, L = {2}", m_H, m_S, m_L);
        }

        /// <summary>
        /// Gets the values.
        /// </summary>
        /// <returns></returns>
        public override object[] GetValues()
        {
            return new object[] { m_H, m_S, m_L };
        }

        /// <summary>
        /// Sets the values.
        /// </summary>
        /// <param name="_aObject">The _a object.</param>
        public override void SetValues(object[] _aObject)
        {
            this.m_H = (double)_aObject[0];
            this.m_S = (double)_aObject[0];
            this.m_L = (double)_aObject[0];
        }

        /// <summary>
        /// create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new HSL(this);
        }
    }
}
